Kit.js ➔ ... ➔ setTimeout   B
last analyzed

Complexity

Conditions 4
Paths 15

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
c 2
b 1
f 0
nc 15
nop 0
dl 0
loc 27
rs 8.5806
1
'use strict'
2
3
const util = require('../util')
4
const output = require('../output')
5
const helpers = require('../run-helpers')
6
const pkg = require('../../package.json')
7
8
const fs = require('fs')
9
const os = require('os')
10
const path = require('path')
11
const https = require('https')
12
const name = require('git-user-name')
13
const email = require('git-user-email')
14
const child = require('child_process')
15
const chalk = require('chalk')
16
const unzip = require('unzip-stream')
17
const rimraf = require('rimraf')
18
const jsonfile = require('jsonfile')
19
const mkdirp = require('mkdirp')
20
const compareVersions = require('compare-versions')
21
22
var Kit = {}
23
24
function extractZip (response, zipName, containerPath, callback, namespace, url) {
25
  // Create cache directory
26
  var tmpdir = path.join(os.tmpdir(), 'inc-v' + pkg.version, namespace)
27
  try {
28
    mkdirp.sync(tmpdir)
29
  } catch (e) {
30
    return callback(e)
31
  }
32
33
  return response
34
    .pipe(unzip.Extract({
35
      path: tmpdir
36
    }))
37
    .on('error', function (err) {
38
      callback(err)
39
    })
40
    .on('close', function () {
41
      setTimeout(function () {
42
        try {
43
          // At this point, there is a good possibility that all files got
44
          // extracted to a subdirectory, which we should correct
45
          var zipPath = path.join(tmpdir, zipName)
46
          if (!fs.existsSync(zipPath)) {
47
            callback(new Error('Error extracting zip file'))
48
            return
49
          }
50
51
          // Check requirements
52
          var json = jsonfile.readFileSync(path.join(zipPath, 'module.json'))
53
          var res = Kit.checkRequirements(json, namespace, url, zipPath)
54
          if (res !== true) {
55
            callback(res)
56
            rimraf.sync(zipPath)
57
            process.exit()
58
          }
59
60
          fs.renameSync(zipPath, path.resolve(containerPath))
61
62
          // Calling back home
63
          callback(null)
64
        } catch (e) {
65
          callback(e)
66
        }
67
      }, 500)
68
    })
69
}
70
71
function downloadZip (zipUrl, zipName, containerPath, callback, namespace, origUrl) {
72
  https.get(zipUrl, function (response) {
73
    if (response.statusCode >= 200 && response.statusCode < 300) {
74
      extractZip(response, zipName, containerPath, callback, namespace, origUrl || zipUrl)
75
      return
76
    }
77
    if (response.headers.location) {
78
      downloadZip(response.headers.location, zipName, containerPath, callback, namespace, zipUrl)
79
      return
80
    }
81
82
    callback(new Error(response.statusCode + ' ' + response.statusMessage))
83
  }).on('error', function (err) {
84
    callback(err)
85
  })
86
}
87
88
function postProcess (slug) {
89
  // Remove stock files
90
  try { fs.unlinkSync(path.join(slug, 'LICENSE.md')) } catch (e) {}
91
  try { fs.unlinkSync(path.join(slug, 'LICENSE')) } catch (e) {}
92
  try { fs.unlinkSync(path.join(slug, 'README')) } catch (e) {}
93
  try { rimraf.sync(path.join(slug, '.git')) } catch (e) {}
94
95
  // Write readme.md
96
  fs.writeFileSync(path.join(slug, 'README.md'), '# ' +
97
    util.boxNameDisplay(slug) + '\n\nA Incluable app for ' + util.boxNameDisplay(slug) + '.', 'utf8')
98
99
  // Download IDE helper
100
  var ideSpinner = output.wait('Downloading code completion helper')
101
  util.downloadHelper(function () {
102
    ideSpinner(true)
103
104
    // Git init
105
    var gitSpinner = output.wait('Initializing local Git repository')
106
    child.exec('cd "' + slug + '" && git init', function (err) {
107
      gitSpinner(err ? err : true)
108
      output.newline()
109
      output.success('To get started:')
110
      output.write('\n' + chalk.bold.magenta('  cd ' + slug) + chalk.gray(' to enter your new module\'s directory\n'))
111
      output.write(chalk.bold.magenta('  inc run') + chalk.gray(' to run your module in the browser\n\n\n'))
112
    })
113
  }, slug)
114
}
115
116
Kit.download = function (kitName, targetPath, callback) {
117
  var containerPath = path.resolve(targetPath)
118
  var kitNameResolved = this.resolveName(kitName)
119
  var namespace = kitNameResolved.split('/')[0]
120
  var kitNameSimplified = kitNameResolved.split('/')[1]
121
  var zipUrl = 'https://github.com/' + kitNameResolved + '/archive/master.zip'
122
123
  downloadZip(zipUrl, kitNameSimplified + '-master', containerPath, callback, namespace)
124
}
125
126
Kit.resolveName = function (kitName) {
127
  kitName = kitName.replace(/\s/ig, '')
128
129
  if (kitName.indexOf('/') > 0) {
130
    // Repo name already includes user/org, so skip any other processing
131
    return kitName
132
  }
133
134
  // Prepend 'starter-'
135
  if (kitName.indexOf('starter-') !== 0) {
136
    kitName = 'starter-' + kitName
137
  }
138
139
  // Prepend org name
140
  return 'includable-modules/' + kitName
141
}
142
143
Kit.checkRequirements = function (json, namespace, url, zipPath) {
144
  // If not exists, no need to check anything
145
  if (!('_starter' in json) || typeof json._starter !== 'object') {
146
    return true
147
  }
148
149
  // Check minimal CLI version to run this starter kit
150
  if ('minVersion' in json._starter && compareVersions(json._starter.minVersion, pkg.version) === 1) {
151
    return 'Starter kit requires version ' + json._starter.minVersion + ' of ' +
152
      'Includable CLI, but you have ' + pkg.version + '. Please update!'
153
  }
154
155
  // Save starter.json meta data
156
  mkdirp.sync(path.join(zipPath, '.inc', 'meta'))
157
  json._starter.sourceUrl = url
158
  json._starter.namespace = namespace
159
  jsonfile.writeFileSync(path.join(zipPath, '.inc', 'meta', 'starter.json'), json._starter)
160
161
  // Remove starter meta data
162
  delete json['_starter']
163
164
  return true
165
}
166
167
Kit.create = function (kitName, slug) {
168
  // Get slug
169
  var userName = (util.getSystemUsername() || 'user').toLowerCase()
170
  if (slug.match(/^[a-z0-9-]+\/[a-z0-9-]+$/)) {
171
    userName = slug.split('/')[0]
172
    slug = slug.split('/')[1]
173
  }
174
  if (!slug.match(/^[a-z0-9-]+$/)) {
175
    output.err('Module name should only contain lowercase letters, numbers and dashes.')
176
    return
177
  }
178
179
  // Check if directory doesn't already exist
180
  var stopSpinner = output.wait('Downloading template \'' + kitName + '\'')
181
  if (fs.existsSync(slug)) {
182
    stopSpinner('Directory \'' + slug + '\' already exists!')
183
    return
184
  }
185
186
  try {
187
    Kit.download(kitName, slug, function (err) {
188
      if (err) {
189
        stopSpinner(err)
190
        return
191
      }
192
      stopSpinner(true)
193
194
      // Write module.json
195
      var json = jsonfile.readFileSync(path.join(slug, 'module.json'))
196
      json = util.moduleJSON(slug, userName, name({
197
        type: 'global'
198
      }), email({
199
        type: 'global'
200
      }), json)
201
      fs.writeFileSync(path.join(slug, 'module.json'), JSON.stringify(json, null, 3), 'utf8')
202
203
      // Write composer.json
204
      if (!fs.existsSync(path.join(slug, 'composer.json'))) {
205
        fs.writeFileSync(path.join(slug, 'composer.json'), JSON.stringify({
206
          'require': {}
207
        }, null, 3), 'utf8')
208
      }
209
210
      // Write version file
211
      mkdirp.sync(path.join(slug, '.inc'))
212
      fs.writeFileSync(path.join(slug, '.inc', 'version'), '11', 'utf8')
213
214
      // Scripts
215
      helpers.startChildProcess('post-create', function () {
216
        postProcess(slug)
217
      }, slug)
218
    })
219
  } catch (e) {
220
    stopSpinner(e)
221
  }
222
}
223
224
module.exports = Kit
225